using System;
using System.Collections;
using System.Collections.Generic;
using qip_history_synchronizer.Core;
using qip_history_synchronizer.Mimes;

namespace qip_history_synchronizer.Mimes
{
    /// <summary>
    /// Rfc 2822 3.4 address-list. Rfc defines two types of addresses mailbox and group.
    /// <p/>
    /// <p style="margin-top: 0; margin-bottom: 0"/><b>address-list</b> syntax: address *("," address).
    /// <p style="margin-top: 0; margin-bottom: 0"/><b>address</b> syntax: mailbox / group.
    /// <p style="margin-top: 0; margin-bottom: 0"/><b>mailbox</b> syntax: ['"'dispaly-name'"' ]&lt;localpart@domain&gt;.
    /// <p style="margin-top: 0; margin-bottom: 0"/><b>group</b> syntax: '"'dispaly-name'":' [mailbox *(',' mailbox)]';'.
    /// </summary>
    public class AddressList : IEnumerable
    {
        private HeaderField   m_HeaderField = null;
        private List<Address> m_pAddresses  = null;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public AddressList()
        {
            m_pAddresses = new List<Address>();
        }

		
        #region method Add

        /// <summary>
        /// Adds a new address to the end of the collection.
        /// </summary>
        /// <param name="address">Address to add.</param>
        public void Add(Address address)
        {
            address.Owner = this;
            m_pAddresses.Add(address);
		
            OnCollectionChanged();
        }

        #endregion

        #region method Insert

        /// <summary>
        /// Inserts a new address into the collection at the specified location.
        /// </summary>
        /// <param name="index">The location in the collection where you want to add the address.</param>
        /// <param name="address">Address to add.</param>
        public void Insert(int index,Address address)
        {
            address.Owner = this;
            m_pAddresses.Insert(index,address);
	
            OnCollectionChanged();
        }

        #endregion


        #region method Remove
		
        /// <summary>
        /// Removes address at the specified index from the collection.
        /// </summary>
        /// <param name="index">Index of the address which to remove.</param>
        public void Remove(int index)
        {
            Remove((Address)m_pAddresses[index]);
        }

        /// <summary>
        /// Removes specified address from the collection.
        /// </summary>
        /// <param name="address">Address to remove.</param>
        public void Remove(Address address)
        {
            address.Owner = null;
            m_pAddresses.Remove(address);
		
            OnCollectionChanged();
        }

        #endregion

        #region method Clear

        /// <summary>
        /// Clears the collection of all addresses.
        /// </summary>
        public void Clear()
        {
            foreach(Address address in m_pAddresses){
                address.Owner = null;
            }
            m_pAddresses.Clear();

            OnCollectionChanged();
        }

        #endregion


        #region method Parse

        /// <summary>
        /// Parses address-list from string.
        /// </summary>
        /// <param name="addressList">Address list string.</param>
        /// <returns></returns>
        public void Parse(string addressList)
        {
            addressList = addressList.Trim();
				
            StringReader reader = new StringReader(addressList);
            while(reader.SourceString.Length > 0){
                // See if mailbox or group. If ',' is before ':', then mailbox
                // Example: xxx@domain.com,	group:xxxgroup@domain.com;
                int commaIndex = TextUtils.QuotedIndexOf(reader.SourceString,','); 
                int colonIndex = TextUtils.QuotedIndexOf(reader.SourceString,':');

                // Mailbox
                if(colonIndex == -1 || (commaIndex < colonIndex && commaIndex != -1)){
                    // Read to ',' or to end if last element
                    MailboxAddress address = MailboxAddress.Parse(reader.QuotedReadToDelimiter(','));
                    m_pAddresses.Add(address);
                    address.Owner = this;
                }
                        // Group
                else{
                    // Read to ';', this is end of group
                    GroupAddress address = GroupAddress.Parse(reader.QuotedReadToDelimiter(';'));
                    m_pAddresses.Add(address);
                    address.Owner = this;

                    // If there are next items, remove first comma because it's part of group address
                    if(reader.SourceString.Length > 0){
                        reader.QuotedReadToDelimiter(',');
                    }
                }
            }

            OnCollectionChanged();
        }

        #endregion


        #region method ToAddressListString
        
        /// <summary>
        /// Convert addresses to Rfc 2822 address-list string.
        /// </summary>
        /// <returns></returns>
        public string ToAddressListString()
        {
            string retVal = "";
            for(int i=0;i<m_pAddresses.Count;i++){
                if(m_pAddresses[i] is MailboxAddress){
                    // For last address don't add , and <TAB>
                    if(i == (m_pAddresses.Count - 1)){
                        retVal += ((MailboxAddress)m_pAddresses[i]).ToMailboxAddressString();
                    }
                    else{
                        retVal += ((MailboxAddress)m_pAddresses[i]).ToMailboxAddressString() + ",\t";
                    }
                }
                else if(m_pAddresses[i] is GroupAddress){
                    // For last address don't add , and <TAB>
                    if(i == (m_pAddresses.Count - 1)){
                        retVal += ((GroupAddress)m_pAddresses[i]).GroupString;
                    }
                    else{
                        retVal += ((GroupAddress)m_pAddresses[i]).GroupString + ",\t";
                    }
                }
            }
            
            return retVal;
        }

        #endregion


        #region internal method OnCollectionChanged

        /// <summary>
        /// This called when collection has changed. Item is added,deleted,changed or collection cleared.
        /// </summary>
        internal void OnCollectionChanged()
        {
            // AddressList is bounded to HeaderField, update header field value
            if(m_HeaderField != null){
                m_HeaderField.Value = this.ToAddressListString();
            }
        }

        #endregion


        #region interface IEnumerator

        /// <summary>
        /// Gets enumerator.
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return m_pAddresses.GetEnumerator();
        }

        #endregion

        #region Properties Implementation

        /// <summary>
        /// Gets all mailbox addresses. Note: group address mailbox addresses are also included.
        /// </summary>
        public MailboxAddress[] Mailboxes
        {
            get{ 
                ArrayList adressesAll = new ArrayList();
                foreach(Address adress in this){
                    if(!adress.IsGroupAddress){
                        adressesAll.Add((MailboxAddress)adress);
                    }
                    else{
                        foreach(MailboxAddress groupChildAddress in ((GroupAddress)adress).GroupMembers){
                            adressesAll.Add((MailboxAddress)groupChildAddress);
                        }
                    }
                }

                MailboxAddress[] retVal = new MailboxAddress[adressesAll.Count];
                adressesAll.CopyTo(retVal);

                return retVal;
            }
        }

        /// <summary>
        /// Gets address from specified index.
        /// </summary>
        public Address this[int index]
        {
            get{ return (Address)m_pAddresses[index]; }
        }

        /// <summary>
        /// Gets address count in the collection.
        /// </summary>
        public int Count
        {
            get{ return m_pAddresses.Count; }
        }


        /// <summary>
        /// Bound address-list to specified header field.
        /// </summary>
        internal HeaderField BoundedHeaderField
        {
            get{ return m_HeaderField; }

            set{m_HeaderField = value; }
        }

        #endregion

    }
}